using System;
using FireStarter.Base;
using FireStarterModeller.BO;

namespace FireStarter.CodeGeneration.Generator
{
    /// <summary>
    /// Models an IF expression.  The syntax is as follows:
    /// IF(boolean):text to insert if true
    /// 
    /// - boolean - a boolean value obtained by the strategy
    /// - !boolean - reverses the boolean (not indicator)
    /// - stringProperty EQUALS "something" - string comparison (gets string value from strategy)
    /// - stringProperty1 EQUALS stringProperty2 - string comparison
    /// - stringProperty1 EQUALS stringProperty2 EQUALS stringProperty3 - multiple comparisons allowed
    /// - !stringProperty EQUALS "something" - provides a not-equals (once for whole clause)
    /// </summary>
    public class IfExpression : TemplateExpression
    {
        public IfExpression(string propertyName)
        {
            PropertyName = propertyName;
        }

        protected override string GenerateOne(IModelStrategyMapper mapper, IModelElement modelElement)
        {
            bool booleanValue = EvaluateExpression(mapper, modelElement);
            if (booleanValue) return DELETION_MARKER + Part.Generate(mapper, modelElement) + DELETION_MARKER;
            return "";
        }

        public bool EvaluateExpression(IModelStrategyMapper mapper, IModelElement modelElement) {
            string workingPropertyName = PropertyName;
            bool hasNotIndicator = (!String.IsNullOrEmpty(workingPropertyName) && workingPropertyName[0] == '!');
            if (hasNotIndicator) workingPropertyName = workingPropertyName.Substring(1);

            bool booleanValue;
            bool hasEqualCheck =  workingPropertyName != null && workingPropertyName.Contains(" EQUALS ");
            if (hasEqualCheck)
            {
                booleanValue = GetBooleanValueForEqualClause(workingPropertyName, mapper, modelElement);
            }
            else
            {
                booleanValue = GetBooleanValue(workingPropertyName, mapper, modelElement);
            }

            if (hasNotIndicator) booleanValue = !booleanValue;
            return booleanValue;
        }

        private static bool GetBooleanValueForEqualClause(string propertyClause, IModelStrategyMapper mapper, IModelElement modelElement)
        {
            IGenerationStrategy strategy = mapper.GetStrategy(modelElement.GetType());
            string[] parts = propertyClause.Split(new[]{" EQUALS "}, StringSplitOptions.None);
            object[] parsedParts = new object[parts.Length];

            for (int partNum = 0; partNum < parts.Length; partNum++)
            {
                string stringPart = parts[partNum];

                if (Utilities.StringIsWrappedInQuotes(stringPart))
                {
                    parsedParts[partNum] = stringPart.Substring(1, stringPart.Length - 2);
                }
                else if (String.Compare(stringPart, "NULL", true) == 0)
                {
                    parsedParts[partNum] = null;
                } else 
                {
                    parsedParts[partNum] = strategy.GetValue(mapper, modelElement, stringPart);
                }
            }

            for (int partNum = 1; partNum < parts.Length; partNum++)
            {
                object expectedValue = parsedParts[0];
                object compareValue = parsedParts[partNum];
                if (expectedValue == null && compareValue != null) return false;
                if (expectedValue != null && !expectedValue.Equals(compareValue)) return false;
            }
            return true;
        }

        private static bool GetBooleanValue(string propertyName, IModelStrategyMapper mapper, IModelElement modelElement)
        {
            IGenerationStrategy strategy = mapper.GetStrategy(modelElement.GetType());
            bool booleanValue;
            object value = strategy.GetValue(mapper, modelElement, propertyName);

            if (value is bool)
            {
                booleanValue = (bool)value;
            }
            else
            {
                if (!bool.TryParse(Convert.ToString(value), out booleanValue))
                {
                    throw new GenerationException(string.Format(
                                                      "During generation of an IF statement, the property '{0}' did not return a value that could be cast to a boolean.",
                                                      propertyName));
                }
            }
            return booleanValue;
        }

        public string PropertyName { get; private set; }

        public TemplateExpression Part { get; set; }
    }
}